package com.by.basicgateway.nacoslistener;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.by.basicgateway.event.NacosGatewayRouteChangeEventOperate;
import com.by.basicgateway.properties.ByGatewayNacosBasicProperties;
import com.by.basicgateway.properties.ByGatewayNacosProperties;
import com.by.basicgateway.utils.ConfigFileConvertUtil;
import com.google.common.collect.Lists;
import jakarta.annotation.PostConstruct;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.HibernateValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

import static com.by.toolkit.utils.ExceptionUtil.getStackTrace;

/**
 * @author Tian Wei
 * @version 1.0
 * @date 2023/12/20 1:27 PM
 */
@Data
@Slf4j
@Component
public class NacosGatewayRouteListener {

    private static final Validator VALIDATOR = Validation.byProvider(HibernateValidator.class)
            .configure()
            .failFast(false)
            .buildValidatorFactory()
            .getValidator();

    @Autowired
    private NacosGatewayRouteChangeEventOperate nacosGatewayRouteChangeEventOperate;

    @Autowired
    private ByGatewayNacosProperties byGatewayNacosProperties;

    private static final String GATEWAY_PREFIX = "gateway";

    /**
     * 机器启动初始化路由信息
     *
     * @throws NacosException
     */
    @PostConstruct
    public void initGateRouteConfig() throws NacosException {
        ByGatewayNacosBasicProperties propertiesRoute = byGatewayNacosProperties.getRoute();

        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, propertiesRoute.getServerAddr());
        if (StringUtils.isNotBlank(propertiesRoute.getNamespace())) {
            properties.put(PropertyKeyConst.NAMESPACE, propertiesRoute.getNamespace());
        }
        ConfigService configService = NacosFactory.createConfigService(properties);
        String configContent = configService.getConfig(propertiesRoute.getDataId(), propertiesRoute.getGroup(), propertiesRoute.getTimeout());
        List<RouteDefinition> routeDefinitions = ConfigFileConvertUtil.converterListByYaml(GATEWAY_PREFIX, configContent, RouteDefinition.class);
        if (CollectionUtil.isEmpty(routeDefinitions)) {
            log.info("[NacosGatewayRouteListener][initGateRouteConfig] 未匹配到任何路由配置信息");
            return;
        }

        List<Runnable> runnableList = Lists.newArrayListWithExpectedSize(routeDefinitions.size());

        for (RouteDefinition routeDefinition : routeDefinitions) {

            runnableList.add(() -> {
                log.info("[NacosGatewayRouteListener][initGateRouteConfig] 开始初始化路由信息:{}", JSON.toJSONString(routeDefinition));
                nacosGatewayRouteChangeEventOperate.add(routeDefinition);
                log.info("[NacosGatewayRouteListener][initGateRouteConfig] 结束初始化路由信息:{}", JSON.toJSONString(routeDefinition));
            });
        }
        nacosGatewayRouteChangeEventOperate.refreshAll(runnableList);

        //添加配置变更监听器
        try {
            configService.addListener(propertiesRoute.getDataId(), propertiesRoute.getGroup(), new Listener() {
                @Override
                public void receiveConfigInfo(String configContent) {
                    updateGatewayConfig(configContent);
                }
                @Override
                public Executor getExecutor() {
                    //线程
                    return null;
                }
            });
        } catch (NacosException e) {
            log.error("从nacos接收动态路由配置出错!!!,异常信息:{}", getStackTrace(e));
        }

    }

    /**
     * nacos路由变跟后路由信息更新
     *
     * @param configContent
     */
//    @NacosConfigListener(dataId =DATA_ID,type =YAML)
    public synchronized void updateGatewayConfig(String configContent) {
        log.info("[NacosGatewayRouteListener][updateGatewayConfig] 路由变更通知");
        List<RouteDefinition> routeDefinitions = ConfigFileConvertUtil.converterListByYaml(GATEWAY_PREFIX, configContent, RouteDefinition.class);
        if (CollectionUtil.isEmpty(routeDefinitions)) {
            log.info("[NacosGatewayRouteListener][updateGatewayConfig] 未匹配到任何路由配置信息");
            return;
        }
        //拷贝数据,
        Map<String, RouteDefinition> oldRouteInfoMap = NacosGatewayRouteChangeEventOperate.getCurrentRouteInfoMap();
        //待编辑的路由信息
        List<Runnable> runnableList = Lists.newArrayList();
        //执行路由新增或修改操作
        for (RouteDefinition newRouteDefinition : routeDefinitions) {

            if (!validParam(newRouteDefinition)) {
                continue;
            }
            String id = newRouteDefinition.getId();
            RouteDefinition oldRouteDefinition = oldRouteInfoMap.remove(id);
            if (Objects.nonNull(oldRouteDefinition)) {
                runnableList.add(() -> {
                    log.info("[NacosGatewayRouteListener][updateGatewayConfig] 开始修改路由信息:{}", JSON.toJSONString(newRouteDefinition));
                    nacosGatewayRouteChangeEventOperate.updateById(newRouteDefinition);
                    log.info("[NacosGatewayRouteListener][updateGatewayConfig] 结束修改路由信息:{}", JSON.toJSONString(newRouteDefinition));
                });
                oldRouteInfoMap.remove(id);
            }

            if (Objects.isNull(oldRouteDefinition)) {
                runnableList.add(() -> {
                    log.info("[NacosGatewayRouteListener][adddGatewayConfig] 开始新增路由信息:{}", JSON.toJSONString(newRouteDefinition));
                    nacosGatewayRouteChangeEventOperate.add(newRouteDefinition);
                    log.info("[NacosGatewayRouteListener][adddGatewayConfig] 结束修改路由信息:{}", JSON.toJSONString(newRouteDefinition));

                });
            }
        }
        //执行路由删除操作
        for (String deletedId : oldRouteInfoMap.keySet()) {
            runnableList.add(() -> {
                log.info("[NacosGatewayRouteListener][deletedGatewayConfig] 开始删除路由信息:{}", deletedId);
                nacosGatewayRouteChangeEventOperate.delete(deletedId);
                log.info("[NacosGatewayRouteListener][deletedGatewayConfig] 结束删除路由信息:{}", deletedId);
            });
        }
        nacosGatewayRouteChangeEventOperate.refreshAll(runnableList);
    }

    /**
     * 必要参数校验
     *
     * @param routeDefinition
     * @return
     */
    private static Boolean validParam(RouteDefinition routeDefinition) {

        String id = routeDefinition.getId();
        if (StringUtils.isEmpty(id)) {
            return Boolean.FALSE;
        }

        Set<ConstraintViolation<RouteDefinition>> validates = VALIDATOR.validate(routeDefinition);
        if (CollectionUtil.isEmpty(validates)) {
            return Boolean.TRUE;
        }
        String errorMessage = validates.stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(","));
        log.error("[NacosGatewayRouteListener][validParam] 路由信息配置错误,必要传参数为空,路由对象:[{}],丢失信息:[{}]", JSON.toJSONString(routeDefinition), errorMessage);
        return Boolean.FALSE;
    }

}
